package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"strings"
)

func main() {
	/*
		Go语言为方便开发者使用，将IO操作封装在了如下几个包中：
			io 为 IO 原语提供基本的接口
			io / ioutil 封装一些实用的 I/O 函数
			fmt 实现格式化 I/O
			bufio 实现带缓冲 I/O

			bufio 包实现了有缓冲的 I/O。
			它包装一个 io.Reader 和 io.Writer 接口对象，创建另一个也实现了该接口，且同时还提供了缓冲和一些文本文件 I/O 的帮助函数的 对象。

		1.bufio包 原理
			io操作 本身的效率并不低，低的是频繁的访问本地磁盘的文件。
			所以bufio就提供了 缓冲区（分配一块内存）。读和写都先在缓冲区，最后再读写文件，来降低访问本地磁盘的次数，从而提高效率。

								读取小于buf的内容				当buf为空时，一次性读取 buf 大小的内容
						程序	<—————————————————— 缓冲区（buf） <—————————————————————————————————— 文件
			        （program.go）<——————————————————————————————————————————————————————————————————（file）
												读取大于 buf 的内容

								写入小于buf的内容				当buf没有足够空间的时候，把缓冲区内容写入文件（清空缓冲区）
						程序	——————————————————> 缓冲区（buf） ——————————————————————————————————> 文件
			        （program.go）——————————————————————————————————————————————————————————————————>（file）
												大于 buf 直接写入文件


		2.type Reader
		（1）基础介绍：
			bufio.Reader 是 bufio 对 io.Reader 的封装
				type Reader struct {
					buf          []byte
					rd           io.Reader // 客户提供的阅读器
					r, w         int       // buf read and write positions （位置）
					err          error
					lastByte     int // 读取未读取字节的最后一个字节；-1表示无效
					lastRuneSize int // 未读取的最后一个字符的大小；-1表示无效
				}

			bufio.Read(p []byte) 相当于读取大小 len§ 的内容：
			1.当缓存区有内容时，将缓存区内容全部填入 p 并清空缓存区
			2.当缓存区没有内容时，且 len§ > len(buf)，即要读取的内容比缓存区还要大，直接去读取即可
			3.当缓存区没有内容时，且 len§ < len(buf)，即要读取的内容比缓存区小，缓存区从文件读取内容充满缓存区，并将 p 填满（此时缓存区有剩余内容）
			4.以后再次读取缓存区有内容，将缓存区内容全部填入 p 并清空缓存区（此时和情况1一样）
			5.Reader 内部通过维护一个 r, w 即读入和写入的位置索引来判断是否缓冲区内容被全部读出

		（2）方法
			1.func NewReaderSize(rd io.Reader, size int) *Reader
				将 rd 封装成一个带缓存的 bufio.Reader 对象，缓存大小由 size 指定（如果小于 16 会被设置为 16）。
				如果 rd 的基类型就是有足够缓存的 bufio.Reader 类型，则直接将 rd 转换为基类型

			2.func NewReader(rd io.Reader) *Reader
				NewReader 相当于 NewReaderSize(rd, 4096)

			3.func (* Reader) Reset(r io.Reader)
				Reset 丢弃缓冲区中的数据，清除任何错误，将 b 重设为其下层从 r 读取数据

			4.func (b *Reader) Read(p []byte) (n int, err error)
				读取数据写入 p。返回写入 p 的字节数。
				本方法一次调用最多会调用下层 Reader 接口一次 Read方法，
				因此返回值 n 可能小于 len。读取到达结尾时，返回值 n 将为 0 而 err 将为 io.EDF

			5.func (b *Reader) Peek(n int) ([]byte, err)
				返回缓存的一个切片，该切片引用缓存中前 n 个字节的数据，
				该操作不会将数据读出，只是引用，引用的数据在下一次读取操作之前是有效的。
				如果切片长度小于 n，则返回一个错误信息说明原因。
				如果 n 大于缓存的总大小，则返回 ErrBufferFull。

			6.func (b *Reader) ReadByte() (c byte, err error)
				读取并返回一个字节。如果没有可用的数据，会返回错误

			func (b *Reader) ReadBytes(delim byte) ([]byte, error)
				读取直到输入中第一次出现 delim，
				返回包含分隔符之前的数据的切片
				如果 ReadBytes 在找到分隔符之前就遇到错误，它返回错误之前读取的数据和错误本身（通常为 io.EOF）
				error != nil 当且仅当 返回的数据不以 delim 结尾时。

			7.func (b *Reader) UnreadByte() error
				吐出最近一次读取操作读取的最后一个字节。（只能吐出最后一个，多次调用会出问题）

			8.func (b *Reader) ReadRune() (r rune, size int, err error)
				读取一个 UTF-8 编码的 unicode 码值，返回该码值、其编码长度、可能的错误。
				如果 UTF-8 编码非法，读取位置只移动 1 字节，返回 U+FFFD，返回值 size 为 1，而 err 为 nil。
				如果没有可用的数据，会返回错误。

			9.func (b *Reader) UnreadRune() error
				吐出最后一次 ReadRune 调用读取的 unicode 码值。
				如果最后一次不是调用的 ReadRune，会返回错误。（从这点看 UnreadRune 比 UnreadByte 严格多了）

			10.func (b* Reader) ReadLine() (line []byte, isPrefix bool, err error)
				尝试返回一行数据，不包括行尾标志的字节。
				如果行太长超过了缓冲，返回值 isPrefix 会被设为 true，并返回行的前面一部分。该行剩下的部分将在之后的调用中返回。
						返回值 isPrefix 会在返回该行最后一个片段时才设为 false。
				返回切片是缓冲的子切片，只在下一次读取操作之前有效。
				ReadLine 要么返回一个 非nil 的 line，要么返回一个 非nil 的 err，两个返回值至少一个 非nil

			11.func (b *Reader) ReadSlice(delim byte) (lin []byte, err error)
				读取直到第一次遇到 delim 字节，返回缓冲里的包含已读取的数据和 delim 字节的切片。
				该返回值只在下一次读取操作之前合法。
				如果 ReadLine 在读取到 delim 之前遇到了错误，它会返回在错误之前读取的数据在缓冲的切片以及该错误（一般是 io.EOF）。
				如果在读取 delim 之前缓冲就写满了，ReadSlice 失败并返回 ErrBufferFull。
				因为 ReadSlice 的返回值会被下一次 I/O 操作重写，调用者应尽量使用 ReadBytes 或 ReadString 替代本法。
					当且仅当 ReadBytes 方法返回的切片不以 delim 结尾时，会返回一次 非nil 的错误。

			12.func (b *Reader) ReadString(delim byte) (line string, err error)
				读取直到第一次遇到 delim 字节，返回一个包含已读取的数据和 delim 字节的字符串。
				如果在读取到 delim 之前遇到错误，它会返回在错误之前读取的数据以及该错误（一般是 io.EOF）。
				当且仅当  ReadString 方法返回的切片不以 delim 结尾时，会返回一个 非nil 错误。

			13.func (b *Reader) WriterTo(w io.Writer) (n int64, err error)
				实现了 io.WriterTo 接口
				会将 Reader 内部的数据写入到 w 中，并返回写入的字节数以及可能发生的错误。
				调用该方法时，它会先尝试从Reader中读取数据，然后将读取到的数据写入到w中，直到Reader中的数据全部写完或者发生了错误。
				在整个过程中，该方法会不断更新写入的字节数n，最终返回n和可能发生的错误err

			注：该方法只能在Reader类型的 变量 上调用，而不能直接调用。

	*/
	//NewReader、Reset
	s := strings.NewReader("ABCDEFG")
	str := strings.NewReader("12345")
	br := bufio.NewReader(s)
	b, _ := br.ReadString('\n')
	fmt.Println(b) //ABCDEFG
	br.Reset(str)
	b, _ = br.ReadString('\n')
	fmt.Println(b) //12345

	fmt.Println("-----------------------------")

	//Read
	s1 := strings.NewReader("ABCDEFGHIGKLMNOPQRSTUVWXYZ1234567890")
	br1 := bufio.NewReader(s1)
	p := make([]byte, 10)

	for {
		n, err := br1.Read(p)
		if err == io.EOF {
			fmt.Println(err)
			break
		} else {
			fmt.Printf("string(p[0:n]): %v\n", string(p[0:n]))
		}
	}
	/*结果：
	string(p[0:n]): ABCDEFGHIG
	string(p[0:n]): KLMNOPQRST
	string(p[0:n]): UVWXYZ1234
	string(p[0:n]): 567890
	EOF
	*/

	fmt.Println("----------------------------------")

	//Peek
	s2 := strings.NewReader("0123456789")
	br2 := bufio.NewReaderSize(s2, 16) //16 为设定的缓冲区大小（最小为16）
	//切片 p 长度小于 n，返回错误信息说明原因
	p, err := br2.Peek(12) //获取缓存中的前 12 个字符（12 > len(s:"0123456789")=10）
	if err != nil {
		fmt.Println(err) //EOF
	}
	fmt.Printf("%s\n", p) //0123456789

	//n 大于缓存的总大小，则返回 ErrBufferFull
	p1, err := br2.Peek(17) //获取缓存中的前 17 个字符（17 > 大于缓冲区的大小=16）
	if err != nil {
		fmt.Println(err) //bufio: buffer full
	}
	fmt.Printf("%s\n", p1) //0123456789

	fmt.Println("----------------------------------------")

	//ReadByte
	s3 := strings.NewReader("qwertyuiopasdfghj[]")
	br3 := bufio.NewReader(s3)

	for {
		c, err := br3.ReadByte()
		if err != nil {
			fmt.Println(err) //EOF
			break
		}
		fmt.Printf("%c ", c)
	} //q w e r t y u i o p a s d f g h j [ ] EOF
	fmt.Println()

	fmt.Println("----------------------------------------------")
	s4 := strings.NewReader("ABCDEFG")
	br4 := bufio.NewReader(s4)

	c, _ := br4.ReadByte()
	fmt.Printf("%c\n", c) //A

	c, _ = br4.ReadByte()
	fmt.Printf("%c\n", c) //B

	err = br4.UnreadByte() //已经读取的’B‘，被吐回了缓冲区（只能吐出最近一次读取的最后一个字节，多次调用会出问题）
	fmt.Println(err)       //<nil>
	c, _ = br4.ReadByte()
	fmt.Printf("%c\n", c) //B
	/*结果：
	A
	B
	<nil>
	B
	*/

	fmt.Println("-----------------------------------")

	//ReadRune、UnreadRune
	//调用 UnreadRune 前最后一次调用的是 ReadRune
	b5 := strings.NewReader("abcdefg")
	br5 := bufio.NewReader(b5)

	r, size, _ := br5.ReadRune()
	fmt.Printf("%c %v\n", r, size) //a 1

	r, size, _ = br5.ReadRune()
	fmt.Printf("%c %v\n", r, size) //b 1

	err = br5.UnreadByte() //吐出最后一次调用ReadRune读取的 unicode 码值，error值为 nil
	fmt.Println(err)       //<nil>
	r, size, _ = br5.ReadRune()
	fmt.Printf("%c %v\n", r, size) //b 1
	/*结果：
	a 1
	b 1
	nil
	b 1
	*/

	fmt.Println("--------------")

	//调用 UnreadRune 前最后一次调用的不是 ReadRune
	r, size, _ = br5.ReadRune() //c 1
	fmt.Printf("%c %v\n", r, size)

	c, _ = br5.ReadByte()
	fmt.Printf("%c\n", c) //d

	err = br5.UnreadRune() //不会吐出最后一次读取的 unicode码值，会返回错误
	fmt.Println(err)       //bufio: invalid use of UnreadRune
	r, size, _ = br5.ReadRune()
	fmt.Printf("%c %v\n", r, size) //e 1
	/*结果：
	c 1
	d
	bufio: invalid use of UnreadRune
	e 1
	*/

	fmt.Println("--------------------------------------")

	//ReadLine
	s6 := strings.NewReader("喜羊羊\r灰太狼\n孙悟空\n汤姆\r杰瑞")
	br6 := bufio.NewReader(s6)

	w, isPerfix, _ := br6.ReadLine()
	fmt.Printf("%q %v\n", w, isPerfix) //"喜羊羊\r灰太狼" false

	w, isPerfix, _ = br6.ReadLine()
	fmt.Printf("%q,%v\n", w, isPerfix) //"孙悟空",false

	w, isPerfix, _ = br6.ReadLine()
	fmt.Printf("%q,%v\n", w, isPerfix) //"汤姆\r杰瑞",false
	/*结果：
	"喜羊羊\r灰太狼" false
	"孙悟空",false
	"汤姆\r杰瑞",false
	*/

	fmt.Println("-------------------------------------")
	s7 := strings.NewReader("xyy,myy,lyy,htl")
	br7 := bufio.NewReader(s7)

	w, _ = br7.ReadSlice(',')
	fmt.Printf("%q\n", w) //"xyy,"

	w, _ = br7.ReadSlice(',')
	fmt.Printf("%q\n", w) //"myy,"

	w, _ = br7.ReadSlice(',')
	fmt.Printf("%q\n", w) //"lyy,"
	/*结果：
	"xyy,"
	"myy,"
	"lyy,"
	*/

	fmt.Println("-----------------------------------------------")

	//ReadString
	s8 := strings.NewReader("xyy myy lyy htl")
	br8 := bufio.NewReader(s8)

	s0, _ := br8.ReadString(' ')
	fmt.Printf("%q\n", s0)

	s0, _ = br8.ReadString(' ')
	fmt.Printf("%q\n", s0)

	s0, _ = br8.ReadString(' ')
	fmt.Printf("%q\n", s0)
	/*结果：
	"xyy "
	"myy "
	"lyy "
	*/

	fmt.Println("----------------------------------------------")
	s9 := strings.NewReader("ADHSKFHSVBDSFHIOYI")
	br9 := bufio.NewReader(s9)
	b0 := bytes.NewBuffer(make([]byte, 0))

	br9.WriteTo(b0)
	fmt.Printf("%s\n", b0) //ADHSKFHSVBDSFHIOYI
}
